package com.sakura.oj.service.impl;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sakura.oj.common.ErrorCode;
import com.sakura.oj.constant.CommonConstant;
import com.sakura.oj.exception.BusinessException;
import com.sakura.oj.judge.codesandbox.model.JudgeInfo;
import com.sakura.oj.mapper.ProblemSubmitMapper;
import com.sakura.oj.model.dto.problem.ProblemSubmitRequest;
import com.sakura.oj.model.dto.submit.SubmitQueryRequest;
import com.sakura.oj.model.entity.Problem;
import com.sakura.oj.model.entity.ProblemSubmit;
import com.sakura.oj.model.entity.User;
import com.sakura.oj.model.enums.ProblemSubmitLanguageEnum;
import com.sakura.oj.model.enums.ProblemSubmitStatusEnum;
import com.sakura.oj.model.vo.*;
import com.sakura.oj.service.ProblemService;
import com.sakura.oj.service.ProblemSubmitService;
import com.sakura.oj.service.UserService;
import com.sakura.oj.utils.SqlUtils;
import com.sakura.oj.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author Sakura
 * @description 针对表【problem_submit(题目提交表)】的数据库操作Service实现
 * @createDate 2025-03-19 20:27:42
 */
@Slf4j
@Service
public class ProblemSubmitServiceImpl extends ServiceImpl<ProblemSubmitMapper, ProblemSubmit>
        implements ProblemSubmitService {

    @Resource
    private ProblemService problemService;
    @Resource
    private UserService userService;

    @Resource
    private ProblemSubmitMapper baseMapper;

    /**
     * 将字符串字段名映射到 SFunction
     *
     * @param sortField 排序字段字符串
     * @return 对应的 SFunction，如果字段无效则返回 null
     */
    private static SFunction<ProblemSubmit, ?> mapFieldStringToSFunction(String sortField) {
        if (StringUtils.isBlank(sortField)) {
            return null;
        }
        return switch (sortField) {
            case "createTime" -> ProblemSubmit::getCreateTime;
            case "updateTime" -> ProblemSubmit::getUpdateTime;
            // 如果有其他需要排序的字段，请在此处添加 case
            default ->
                // 对于无法识别的字段，可以返回 null 或者一个默认排序字段
                // 返回 null 的话，调用方需要处理这种情况
                    null;
        };
    }

    // todo 后期使用redis做幂等，多次提交拦截，让数据库只生成一条记录，避免重复提交
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long doProblemSubmit(Long userId, ProblemSubmitRequest form) {

        // 判断传入的语言是否合法
        String language = form.getLanguage();
        ProblemSubmitLanguageEnum languageEnum = ProblemSubmitLanguageEnum.getEnumByValue(language);
        if (languageEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "不支持的编程语言");
        }

        // 1. 查询题目是否存在
        Long problemId = form.getProblemId();
        Problem problem = problemService.getById(problemId);
        if (ObjectUtils.isEmpty(problem)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }

        ProblemSubmit problemSubmit = new ProblemSubmit();
        problemSubmit.setProblemId(problemId);
        problemSubmit.setUserId(userId);
        problemSubmit.setLanguage(language);
        problemSubmit.setCode(form.getCode());
        JudgeInfo judgeInfo = new JudgeInfo();
        problemSubmit.setJudgeInfo(JSONUtil.toJsonStr(judgeInfo));
        problemSubmit.setStatus(ProblemSubmitStatusEnum.WAITING.getValue());

        boolean save = this.save(problemSubmit);
        if (!save) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目提交失败");
        }
        // 修改提交数
        Problem updateProblem = new Problem();
        updateProblem.setId(problemId);
        updateProblem.setSubmitNum(problem.getSubmitNum() + 1);
        boolean update = problemService.updateById(updateProblem);
        if (!update)
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "题目提交失败");
        return problemSubmit.getId();
    }

    @Override
    public Page<SubmitVo> searchSubmitPageVo(Page<ProblemSubmit> page, SubmitQueryRequest submitQueryRequest) {
        // 1. 构建查询条件
        Long problemId = submitQueryRequest.getProblemId();
        Long userId = UserHolder.getUserId();

        Problem problem = problemService.getById(problemId);
        if (ObjectUtils.isEmpty(problem)) {
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "题目不存在");
        }

        LambdaQueryWrapper<ProblemSubmit> queryWrapper = getQueryWrapper(submitQueryRequest);
        queryWrapper.eq(ObjectUtils.isNotEmpty(userId), ProblemSubmit::getUserId, userId);

        // 2. 查询数据
        this.page(page, queryWrapper);

        // 3. 封装返回
        return new Page<>() {{
            setTotal(page.getTotal());
            setPages(page.getPages());
            setCurrent(page.getCurrent());
            setSize(page.getSize());
            setRecords(page.getRecords().stream().map(problemSubmit -> {
                SubmitVo submitVo = new SubmitVo();
                submitVo.setId(problemSubmit.getId());
                submitVo.setProblemId(problemSubmit.getProblemId());
                submitVo.setCode(problemSubmit.getCode());
                submitVo.setLanguage(problemSubmit.getLanguage());
                submitVo.setStatus(problemSubmit.getStatus());
                String judgeInfo = problemSubmit.getJudgeInfo();
                submitVo.setJudgeInfo(JSONUtil.toBean(judgeInfo, JudgeInfo.class, true));
                return submitVo;
            }).toList());
        }};
    }

    @Override
    public LambdaQueryWrapper<ProblemSubmit> getQueryWrapper(SubmitQueryRequest submitQueryRequest) {
        Long id = submitQueryRequest.getId();
        Long problemId = submitQueryRequest.getProblemId();
        String language = submitQueryRequest.getLanguage();
        Integer status = submitQueryRequest.getStatus();
        String sortField = submitQueryRequest.getSortField();
        String sortOrder = submitQueryRequest.getSortOrder();

        LambdaQueryWrapper<ProblemSubmit> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(ObjectUtils.isNotEmpty(id), ProblemSubmit::getId, id);
        queryWrapper.eq(ObjectUtils.isNotEmpty(problemId), ProblemSubmit::getProblemId, problemId);
        queryWrapper.eq(ObjectUtils.isNotEmpty(language), ProblemSubmit::getLanguage, language);
        queryWrapper.eq(ObjectUtils.isNotEmpty(status), ProblemSubmit::getStatus, status);

        // 假设有一个方法将字符串字段名转换为 SFunction
        SFunction<ProblemSubmit, ?> columnToOrderBy = mapFieldStringToSFunction(sortField);
        boolean applySort = SqlUtils.validSortField(sortField);

        queryWrapper.orderBy(applySort,
                CommonConstant.SORT_ORDER_ASC.equals(sortOrder),
                columnToOrderBy); // 提供实际的列
        return queryWrapper;
    }

    @Override
    public Page<ProblemSubmitVo> getProblemSubmitVOPage(Page<ProblemSubmit> problemPage) {
        List<ProblemSubmit> records = problemPage.getRecords();
        Set<Long> userIds = records.stream().map(ProblemSubmit::getUserId).collect(Collectors.toSet());
        List<User> users = userService.listByIds(userIds);
        Map<Long, UserVO> userMap = users.stream().collect(Collectors.toMap(User::getId, user -> userService.getUserVO(user)));

        ArrayList<ProblemSubmitVo> problemSubmitVos = new ArrayList<>(records.size());
        for (ProblemSubmit record : records) {
            ProblemSubmitVo problemSubmitVo = new ProblemSubmitVo();
            problemSubmitVo.setId(record.getId());
            problemSubmitVo.setProblemId(record.getProblemId());
            problemSubmitVo.setUserId(record.getUserId());
            problemSubmitVo.setLanguage(record.getLanguage());
            problemSubmitVo.setCode(record.getCode());
            problemSubmitVo.setJudgeInfo(JSONUtil.toBean(record.getJudgeInfo(), JudgeInfo.class, true));
            problemSubmitVo.setStatus(record.getStatus());
            problemSubmitVo.setUserVO(userMap.get(record.getUserId()));
            problemSubmitVo.setCreateTime(record.getCreateTime());
            problemSubmitVo.setUpdateTime(record.getUpdateTime());

            problemSubmitVos.add(problemSubmitVo);
        }

        return new Page<>() {{
            setTotal(problemPage.getTotal());
            setPages(problemPage.getPages());
            setCurrent(problemPage.getCurrent());
            setSize(problemPage.getSize());
            setRecords(problemSubmitVos);
        }};
    }

    @Override
    public boolean checkPass(Long problemId, Long userId) {
        SubmitQueryRequest submitQueryRequest = new SubmitQueryRequest();
        submitQueryRequest.setStatus(ProblemSubmitStatusEnum.ACCEPTED.getValue());
        submitQueryRequest.setProblemId(problemId);
        LambdaQueryWrapper<ProblemSubmit> queryWrapper = getQueryWrapper(submitQueryRequest);
        queryWrapper.eq(ProblemSubmit::getUserId, userId);
        long count = this.count(queryWrapper);
        return count > 0;
    }

    @Override
    public List<DailySubmissionStat> getSubmissionStats(Long userId, LocalDate startDate, LocalDate endDate) {
        log.info("获取用户 {} 在 {} 到 {} 的提交统计", userId, startDate, endDate);
        return baseMapper.selectSubmissionStats(userId, startDate, endDate);
    }

    @Override
    public List<UserRankVo> getScoreRank(Long pageSize) {
        return baseMapper.selectSubmissionScore(pageSize);
    }

    @Override
    public List<Long> getHotProblemIds(Long periodDays, Long limit) {
        return baseMapper.selectHotProblemIds(periodDays, limit);
    }

    @Override
    public List<UserLanguageSubmitVo> getLanguageSubmitCounts(Long userId) {
        return baseMapper.selectLanguageSubmitCounts(userId);
    }

    @Override
    public List<RecentPassVo> getRecentlySubmittedList(Long userId) {
        return baseMapper.selectRecentlySubmittedList(userId);
    }
}




